/*****************************************************************************
 *   Copyright(C)2009-2022 by VSF Team                                       *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the "License");          *
 *  you may not use this file except in compliance with the License.         *
 *  You may obtain a copy of the License at                                  *
 *                                                                           *
 *     http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an "AS IS" BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 ****************************************************************************/

/*============================ INCLUDES ======================================*/

#include "hal/vsf_hal_cfg.h"

#if VSF_HAL_USE_FLASH == ENABLED

#    include "../../__device.h"
#    include "hal/vsf_hal.h"

/*============================ MACROS ========================================*/

#    ifndef VSF_HW_SPI_FLASH_CFG_MULTI_CLASS
#        define VSF_HW_SPI_FLASH_CFG_MULTI_CLASS VSF_FLASH_CFG_MULTI_CLASS
#    endif

#    define VSF_FLASH_CFG_IMP_PREFIX        vsf_hw_spi
#    define VSF_FLASH_CFG_IMP_UPCASE_PREFIX VSF_HW_SPI

#    define VSF_FLASH_CFG_CAPABILITY_MAX_SIZE     (16u * 1024u)
#    define VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS 0x00010000

/*============================ MACROFIED FUNCTIONS ===========================*/
/*============================ TYPES =========================================*/

// HW
typedef struct VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) {
#    if VSF_HW_SPI_FLASH_CFG_MULTI_CLASS == ENABLED
    vsf_flash_t vsf_flash;
#    endif
    vsf_flash_isr_t      isr;
    vsf_flash_irq_mask_t irq_mask;

    uint8_t buf[VSF_FLASH_CFG_CAPABILITY_MAX_SIZE];
} VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t);
// HW end

/*============================ GLOBAL VARIABLES ==============================*/
/*============================ LOCAL VARIABLES ===============================*/
/*============================ PROTOTYPES ====================================*/
/*============================ IMPLEMENTATION ================================*/

static void VSF_MCONNECT(__, VSF_USART_CFG_IMP_PREFIX, _flash_irqhandler)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr,
    vsf_flash_irq_mask_t irq_mask)
{
    VSF_HAL_ASSERT(NULL != flash_ptr);

    vsf_flash_isr_t *isr_ptr = &flash_ptr->isr;
    if ((irq_mask != 0) && (isr_ptr->handler_fn != NULL)) {
        isr_ptr->handler_fn(isr_ptr->target_ptr, (vsf_flash_t *)flash_ptr,
                            irq_mask);
    }
}

vsf_err_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_init)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr,
    vsf_flash_cfg_t *cfg_ptr)
{
    VSF_HAL_ASSERT(flash_ptr != NULL);
    // configure according to cfg_ptr
    flash_ptr->isr = cfg_ptr->isr;
    // configure interrupt according to cfg_ptr->isr

    memset(flash_ptr->buf, 0xFF, sizeof(flash_ptr->buf));

    return VSF_ERR_NONE;
}

void VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_fini)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr)
{
    VSF_HAL_ASSERT(flash_ptr != NULL);
}

fsm_rt_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_enable)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr)
{
    VSF_HAL_ASSERT(flash_ptr != NULL);
    return fsm_rt_cpl;
}

fsm_rt_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_disable)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr)
{
    VSF_HAL_ASSERT(flash_ptr != NULL);
    return fsm_rt_cpl;
}

void VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_irq_enable)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr,
    vsf_flash_irq_mask_t irq_mask)
{
    flash_ptr->irq_mask |= irq_mask;
    VSF_HAL_ASSERT(flash_ptr != NULL);
}

void VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_irq_disable)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr,
    vsf_flash_irq_mask_t irq_mask)
{
    flash_ptr->irq_mask &= ~irq_mask;
    VSF_HAL_ASSERT(flash_ptr != NULL);
}

vsf_err_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_erase_multi_sector)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr,
    vsf_flash_size_t offset, vsf_flash_size_t size)
{
    VSF_HAL_ASSERT(flash_ptr != NULL);
    VSF_HAL_ASSERT((offset + size) <= (VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS +
                                       VSF_FLASH_CFG_CAPABILITY_MAX_SIZE));

    if (offset < VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS) {
        return VSF_ERR_FAIL;
    }
    offset -= VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS;

    vsf_trace_debug(
        "%s erase offset(0x%08x), size:0x%08x" VSF_TRACE_CFG_LINEEND,
        __FUNCTION__, offset, size);
    
    memset(&flash_ptr->buf[offset], 0xff, size);

    VSF_MCONNECT(__, VSF_USART_CFG_IMP_PREFIX, _flash_irqhandler)
    (flash_ptr, VSF_FLASH_IRQ_ERASE_MASK);

    return VSF_ERR_NONE;
}

vsf_err_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_write_multi_sector)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr,
    vsf_flash_size_t offset, uint8_t *buffer, vsf_flash_size_t size)
{
    VSF_HAL_ASSERT(flash_ptr != NULL);
    VSF_HAL_ASSERT(NULL != buffer);
    VSF_HAL_ASSERT((offset + size) <= (VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS +
                                       VSF_FLASH_CFG_CAPABILITY_MAX_SIZE));

    if (offset < VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS) {
        return VSF_ERR_FAIL;
    }
    offset -= VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS;

    vsf_trace_debug(
        "%s write offset(0x%08x), size:0x%08x" VSF_TRACE_CFG_LINEEND,
        __FUNCTION__, offset, size);
    //vsf_trace_buffer(VSF_TRACE_DEBUG, buffer, size);

    memcpy(&flash_ptr->buf[offset], buffer, size);

    VSF_MCONNECT(__, VSF_USART_CFG_IMP_PREFIX, _flash_irqhandler)
    (flash_ptr, VSF_FLASH_IRQ_WRITE_MASK);

    return VSF_ERR_NONE;
}

vsf_err_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_read_multi_sector)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr,
    vsf_flash_size_t offset, uint8_t *buffer, vsf_flash_size_t size)
{
    VSF_HAL_ASSERT(flash_ptr != NULL);
    VSF_HAL_ASSERT(NULL != buffer);
    VSF_HAL_ASSERT((offset + size) <= (VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS +
                                       VSF_FLASH_CFG_CAPABILITY_MAX_SIZE));

    if (offset < VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS) {
        return VSF_ERR_FAIL;
    }
    offset -= VSF_FLASH_CFG_CAPABILITY_BASE_ADDRESS;

    memcpy(buffer, &flash_ptr->buf[offset], size);

    vsf_trace_debug("%s read offset(0x%08x), size:0x%08x" VSF_TRACE_CFG_LINEEND,
                    __FUNCTION__, (uint32_t)offset, (uint32_t)size);
    //vsf_trace_buffer(VSF_TRACE_DEBUG, buffer, size);

    VSF_MCONNECT(__, VSF_USART_CFG_IMP_PREFIX, _flash_irqhandler)
    (flash_ptr, VSF_FLASH_IRQ_READ_MASK);

    return VSF_ERR_NONE;
}

vsf_flash_status_t VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_status)(
    VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t) * flash_ptr)
{
    return (vsf_flash_status_t){
        .is_busy = 0,
    };
}

/*============================ INCLUDES ======================================*/

/*\note TODO: add comments about template configurations below:
 *  VSF_FLASH_CFG_ERASE_ALL_TEMPLATE
 *  VSF_FLASH_CFG_ERASE_ONE_SECTOR_TEMPLATE
 *  VSF_FLASH_CFG_WRITE_ONE_SECTOR_TEMPLATE
 *  VSF_FLASH_CFG_READ_ONE_SECTOR_TEMPLATE
 */

// only define in source file
#    define VSF_FLASH_CFG_ERASE_ALL_TEMPLATE        ENABLED
#    define VSF_FLASH_CFG_ERASE_ONE_SECTOR_TEMPLATE ENABLED
#    define VSF_FLASH_CFG_WRITE_ONE_SECTOR_TEMPLATE ENABLED
#    define VSF_FLASH_CFG_READ_ONE_SECTOR_TEMPLATE  ENABLED

#    define VSF_FLASH_CFG_IMP_LV0(__IDX, __HAL_OP)                             \
        VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash_t)                       \
        VSF_MCONNECT(VSF_FLASH_CFG_IMP_PREFIX, _flash##__IDX) = {__HAL_OP};
#    include "hal/driver/common/flash/flash_template.inc"

#endif // VSF_HAL_USE_FLASH
